package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/elastic/elastic-transport-go/v8/elastictransport"
	"github.com/elastic/go-elasticsearch/v8"
	"github.com/elastic/go-elasticsearch/v8/esapi"
)

type User struct {
	AccountNum int    `json:"account_number"`
	Address    string `json:"address"`
	Age        uint8  `json:"age"`
	Balance    int    `json:"balance"`
	City       string `json:"city"`
	Email      string `json:"email"`
	Employer   string `json:"employer"`
	FirstName  string `json:"firstname"`
	LastName   string `json:"lastname"`
	State      string `json:"state"`
}

type ELS struct {
	es *elasticsearch.Client
}

func NewELS(es *elasticsearch.Client) *ELS {
	return &ELS{es: es}
}

type H map[string]interface{}

func main() {
	es, err := InitES()
	if err != nil {
		panic(err)
	}

	els := NewELS(es)

	// ctx := context.Background()

	els.Query() // 查询

	// 创建

	// var user = User{
	// 	AccountNum: 9991,
	// 	Address:    "LinHe Street",
	// 	Age:        18,
	// 	Balance:    5472,
	// 	City:       "Guang Zhou",
	// 	FirstName:  "安",
	// 	LastName:   "刘",
	// 	State:      "M",
	// }
	// err = els.Insert(ctx, "user", user)
	// if err != nil {
	// 	fmt.Println(err.Error())
	// }

	// 创建index

	// mapping := H{
	// 	"mappings": H{
	// 		"properties": H{
	// 			"brands_id": H{
	// 				"type": "integer",
	// 			},
	// 			"category_id": H{
	// 				"type": "integer",
	// 			},
	// 			"click_num": H{
	// 				"type": "integer",
	// 			},
	// 			"fav_num": H{
	// 				"type": "integer",
	// 			},
	// 			"id": H{
	// 				"type": "integer",
	// 			},
	// 			"is_hot": H{
	// 				"type": "boolean",
	// 			},
	// 			"is_new": H{
	// 				"type": "boolean",
	// 			},
	// 			"market_price": H{
	// 				"type": "float",
	// 			},
	// 			"name": H{
	// 				"type":     "text",
	// 				"analyzer": "ik_max_word",
	// 			},
	// 			"goods_brief": H{
	// 				"type":     "text",
	// 				"analyzer": "ik_max_word",
	// 			},
	// 			"on_sale": H{
	// 				"type": "boolean",
	// 			},
	// 			"ship_free": H{
	// 				"type": "boolean",
	// 			},
	// 			"shop_price": H{
	// 				"type": "float",
	// 			},
	// 			"sold_num": H{
	// 				"type": "long",
	// 			},
	// 		},
	// 	},
	// }
	//
	// b, err := json.Marshal(mapping)
	// if err != nil {
	// 	fmt.Println(err.Error())
	// }
	//
	// err = els.IndexCreate(ctx, "goods", b)
	// if err != nil {
	// 	fmt.Println(err.Error())
	// }
}

func (e *ELS) IndexCreate(ctx context.Context, index string, body []byte) error {
	req := esapi.IndicesCreateRequest{
		Index: index,
		Body:  bytes.NewBuffer(body),
	}

	res, err := req.Do(ctx, e.es)
	if err != nil {
		return err
	}
	defer func(body io.ReadCloser) {
		_ = body.Close()
	}(res.Body)
	if res.IsError() {
		log.Printf("[%s] Error creating index", res.Status())
	}
	return nil
}

func (e *ELS) Insert(ctx context.Context, index string, info interface{}) error {
	b, err := json.Marshal(info)
	if err != nil {
		return err
	}

	req := esapi.IndexRequest{
		Index: index,
		Body:  bytes.NewBuffer(b),
	}
	res, err := req.Do(ctx, e.es)
	if err != nil {
		return err
	}
	defer func(body io.ReadCloser) {
		_ = body.Close()
	}(res.Body)

	if res.IsError() {
		log.Printf("[%s] Error indexing document", res.Status())
	}
	return nil
}

func (e *ELS) Query() {
	// 3. Search for the indexed documents
	//
	// Build the request body.
	var buf bytes.Buffer
	query := H{
		"query": H{
			"match": H{
				"address": "Street",
			},
		},
	}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := e.es.Search(
		e.es.Search.WithContext(context.Background()),
		e.es.Search.WithIndex("user"),
		e.es.Search.WithBody(&buf),
		e.es.Search.WithTrackTotalHits(true),
		e.es.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(res.Body)

	if res.IsError() {
		var e H
		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
			log.Fatalf("Error parsing the response body: %s", err)
		} else {
			// Print the response status and error information.
			log.Fatalf("[%s] %s: %s",
				res.Status(),
				e["error"].(map[string]interface{})["type"],
				e["error"].(map[string]interface{})["reason"],
			)
		}
	}

	var r H
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		log.Fatalf("Error parsing the response body: %s", err)
	}
	// Print the response status, number of results, and request duration.
	log.Printf(
		"[%s] %d hits; took: %dms",
		res.Status(),
		int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)),
		int(r["took"].(float64)),
	)

	// Print the ID and document source for each hit.
	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
		var user User
		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		marshal, _ := json.Marshal(source)
		err := json.Unmarshal(marshal, &user)
		if err != nil {
			fmt.Println(err.Error())
		}
		// log.Printf(" * ID=%s, %s", hit.(map[string]interface{})["_id"], hit.(map[string]interface{})["_source"])
		fmt.Printf("%+v\n", user)
	}

	log.Println(strings.Repeat("=", 37))
}

func InitES() (*elasticsearch.Client, error) {
	esConf := elasticsearch.Config{
		Addresses: []string{
			"http://192.168.1.2:9200",
		},
		Logger: &elastictransport.ColorLogger{
			Output:            os.Stdout, // 输出到终端
			EnableRequestBody: true,      // 打印请求体(方便查看请求)
		},
	}
	return elasticsearch.NewClient(esConf)
}
